from jyst import JystServlet, Urlable, UserError, discover_yadis_location
from java.sql import SQLException #IGNORE:F0401
from org.openid4java import OpenIDException #IGNORE:F0401
from org.openid4java.message import ParameterList #IGNORE:F0401
#from java.lang import System #IGNORE:F0401

class VirtualUrlable(Urlable):
    def __init__(self):
        Urlable.__init__(self, None)

    def http_get(self, inv):
        con = inv.database_connection()
        delegation = find_delegation(con, inv.request.getServerName())
        #if not delegation is None:
    #    inv.response.setHeader('X-XRDS-Location', delegation.yadis_location)
        inv.send_ok(self.document(inv, delegation))
    
    def http_post(self, inv):
        con = inv.database_connection()
        delegation = find_delegation(con, inv.request.getServerName())
        if inv.has_parameter('sign-out'):
            if delegation is not None:
                inv.request.getSession().removeAttribute('openid-login-' +
                                                         delegation.op_local_id)
            inv.send_ok(self.document(inv, delegation,
                                      'Successfully signed out'))
        else:
            if inv.has_parameter('openid_identifier'):
                user_supplied_id = inv.get_string('openid_identifier').strip()
            elif delegation is None:
                raise UserError('We need an openid_identifier field')
            else:
                user_supplied_id = delegation.op_local_id
            try:
                inv.openid_authenticate(user_supplied_id)
            except UserError, detail:
                detail.document = self.document(inv, delegation)
                raise detail
        
    def document(self, inv, delegation, message=None): #IGNORE:R0201
        doc = inv.create_document()
        root_element = doc.getDocumentElement()
        if message is not None:
            message_element = doc.createElement('message')
            root_element.appendChild(message_element)
            message_element.setTextContent(message)
        if delegation is not None:
            root_element.setAttribute('op-endpoint', delegation.op_endpoint)
            root_element.setAttribute('op-local-id', delegation.op_local_id)
            #root_element.setAttribute('yadis-location',
            #                          delegation.yadis_location)
        return doc
    
    def child(self, path_element):
        if path_element == 'openid-return':
            return OpenIdReturn(self)
        elif path_element == 'xrds':
            return Xrds(self)
        else:
            return Urlable.child(self, path_element)


class Root(JystServlet):
    def init(self, config=None):
        JystServlet.initialize(self, config, 'delegator', 'delegator',
                               VirtualUrlable())
        con = self.services.database_connection()
        stmt = con.createStatement()
        #find db version
        try:
            rslt = stmt.executeQuery("select version from db_version")
            rslt.next()
            db_version = rslt.getInt('version')
        except SQLException:
            db_version = 0
        else:
            rslt.close()
            stmt.close()
            
        if db_version == 0:
            stmt = con.createStatement()
            stmt.executeUpdate("""create table db_version (
                            version integer)""")
            stmt.executeUpdate("insert into db_version values (1)")
            stmt.executeUpdate("""CREATE TABLE openid (
                                identifier character varying(255) primary key,
                                delegate character varying(255) not null,
                                server character varying(255) not null,
                                password character varying(255))""")
            db_version = 1
        if db_version == 1:
            stmt = con.createStatement()
            stmt.executeUpdate('alter table openid drop password')
            stmt.executeUpdate('update db_version set version = 2')
            db_version = 2
        if db_version == 2:
            stmt = con.createStatement()
            stmt.executeUpdate(
                    'alter table openid rename identifier to claimed_host')
            stmt.executeUpdate(
                    'alter table openid rename delegate to op_local_id')
            stmt.executeUpdate(
                    'alter table openid rename server to op_endpoint')
            stmt.executeUpdate('update db_version set version = 3')
            db_version = 3
        if db_version == 3:
            stmt = con.createStatement()
            stmt.executeUpdate('alter table openid add yadis_location ' +
                               'character varying(255)')
            stmt.executeUpdate('update db_version set version = 4')
            db_version = 4
        con.close()
                
    def http_get(self, inv):
        return inv.send_ok(inv.create_document())

    def child(self, path_element):
        if path_element == 'discover':
            return Discover(self)
        else:
            return Urlable.child(self, path_element)

def find_delegation(con, claimed_host):
    delegation = None
    stmt = con.prepareStatement("select claimed_host, op_local_id, \
                op_endpoint, yadis_location from openid where claimed_host = ?")
    stmt.setString(1, claimed_host.rstrip('.'))
    result = stmt.executeQuery()
    if result.next():
        delegation = Delegation(result.getString('claimed_host'),
                                result.getString('op_local_id'),
                                result.getString('op_endpoint'),
                                result.getString('yadis_location'))
    result.close()
    stmt.close()
    return delegation

class Delegation: #IGNORE:R0903
    def __init__(self, claimed_host, op_local_id, op_endpoint, yadis_location):
        self.claimed_host = claimed_host
        self.op_local_id = op_local_id
        self.op_endpoint = op_endpoint
        self.yadis_location = yadis_location

class OpenIdReturn(Urlable):
    def __init__(self, parent):
        Urlable.__init__(self, parent)
    
    def http_get(self, inv): #IGNORE:R0914
        if inv.request.getServerPort() == 80:
            port_part = ''
        else:
            port_part = ':' + str(inv.request.getServerPort())
        inv.response.setHeader('X-XRDS-Location', inv.request.getScheme() +
                '://' + inv.request.getServerName() + port_part +
                inv.request.getContextPath() + '/xrds/')
        try:
            receiving_url = inv.request.getRequestURL()
            query_string = inv.request.getQueryString()
            if query_string is not None and len(query_string) > 0:
                receiving_url.append("?").append(query_string)
                disco = inv.request.getSession().getAttribute("openid-disco")
                verification = inv.services.consumer_manager.verify(
                        receiving_url.toString(),
                        ParameterList(inv.request.getParameterMap()), disco)
                if verification.getVerifiedId() is None :
                    raise UserError('OpenId verification failed. ' +
                                    str(verification.getStatusMsg()))
                identity = str(disco.getClaimedIdentifier())
                con = inv.database_connection()
                delegation = find_delegation(con, inv.request.getServerName())
                op_endpoint = str(disco.getOPEndpoint())
                inv.request.getSession().setAttribute("openid-login-" +
                                identity, (identity, op_endpoint))
                yadis_location = discover_yadis_location(identity)
                if delegation is None:
                    stmt = con.prepareStatement(
                                'insert into openid values (?, ?, ?, ?)')
                    stmt.setString(1, inv.request.getServerName().rstrip('.'))
                    stmt.setString(2, identity)
                    stmt.setString(3, op_endpoint)
                    stmt.setString(4, yadis_location)
                    stmt.executeUpdate()
                    stmt.close()
                    inv.send_ok(self.document(inv,
                            'Successfully set new OP-Local Identifier ' +
                            identity + '.'))
                elif identity != delegation.op_local_id:
                    stmt = con.prepareStatement(
                            'update openid set op_local_id ' +
                            '= ?, op_endpoint = ?, yadis_location = ? ' +
                            'where claimed_host = ?')
                    stmt.setString(1, identity)
                    stmt.setString(2, op_endpoint)
                    stmt.setString(3, yadis_location)
                    stmt.setString(4, inv.request.getServerName().rstrip('.'))
                    stmt.executeUpdate()
                    stmt.close()
                    inv.send_ok(self.document(inv,
                            'Successfully replaced OP-Local Identifier ' +
                            delegation.op_local_id + ' with ' + identity + '.'))
                else:
                    inv.send_see_other(inv.request.getScheme() + '://' +
                            inv.request.getServerName() + ':' +
                            str(inv.request.getServerPort()) +
                            inv.request.getContextPath() +
                            inv.request.getServletPath())
            else:
                inv.send_ok()
        except OpenIDException, detail:
            raise UserError('OpenId error: ' + detail.getMessage())

    def document(self, inv, message): #IGNORE:R0201
        doc = inv.create_document()
        doc_element = doc.getDocumentElement()
        if message is not None:
            message_element = doc.createElement('message')
            doc_element.appendChild(message_element)
            message_element.setTextContent(message)
        return doc

class Xrds(Urlable):
    def __init__(self, users):
        Urlable.__init__(self, users)
    
    def http_get(self, inv):
        #inv.response.setHeader('Content-type', 'application/xrds+xml')
        return inv.send_ok(self.document(inv))
    
    def document(self, inv, message=None): #IGNORE:R0201
        doc = inv.create_document()
        root_element = doc.getDocumentElement()
        if message is not None:
            message_element = doc.createElement('message')
            root_element.appendChild(message_element)
            message_element.setTextContent(message)
        '''
        con = inv.database_connection()
        delegation = find_delegation(con, inv.request.getServerName())
        if delegation is not None:
            root_element.setAttribute('op-endpoint', delegation.op_endpoint)
            root_element.setAttribute('op-local-id', delegation.op_local_id)
            root_element.setAttribute('yadis-location',
                                      delegation.yadis_location)
        ''' #IGNORE:W0105
        return doc

class Discover(Urlable):
    def __init__(self, parent):
        Urlable.__init__(self, parent)
    
    def http_get(self, inv): #IGNORE:R0914
        inv.send_ok(self.document(inv))
            
    def document(self, inv): #IGNORE:R0201
        doc = inv.create_document()
        doc_element = doc.getDocumentElement()
        if inv.has_parameter('openid_identifier'):
            user_supplied_id = inv.get_string('openid_identifier')
            discovered = inv.openid_discovered(user_supplied_id)
            doc_element.setAttribute('op-local-id',
                                     str(discovered.getClaimedIdentifier()))
            doc_element.setAttribute('op-endpoint',
                                     str(discovered.getOPEndpoint()))
            doc_element.setAttribute('yadis-location',
                    discover_yadis_location(
                    str(discovered.getClaimedIdentifier())))
        return doc
